
#if !defined( DeleDAPI_h )
#define DeleDAPI_h

#pragma warning(disable: 4996)  //  deprecated APIs

#include <assert.h>
#include <algorithm>

//  preliminary type declarations, for the functions
typedef unsigned int uint;
typedef unsigned char uchar;
typedef struct TCbData * PCbData;
typedef unsigned (__stdcall * TCallback)(PCbData);
template<typename T> class AutoDispose;
class IScene;


//  Actual functions

AutoDispose<IScene> NewScene(TCallback cb);
char const * DeleDError(uint error);


//  Actual type declarations

class Float2
{
  public:
    float u_, v_;
    Float2() : u_(0), v_(0)
    {
    }
    Float2(float u, float v) : u_(u), v_(v)
    {
    }
    float * begin()
    {
      return &u_;
    }
    float * end()
    {
      return &v_ + 1;
    }
};

class Float3
{
  public:
    float x_, y_, z_;
    Float3() : x_(0), y_(0), z_(0)
    {
    }
    Float3(float x, float y, float z) : x_(x), y_(y), z_(z)
    {
    }
    float * begin()
    {
      return &x_;
    }
    float * end()
    {
      return &z_ + 1;
    }
};

class Color
{
  public:
    uchar b_, g_, r_, a_;
    Color() : b_(0), g_(0), r_(0), a_(0)
    {
    }
    Color(uchar r, uchar g, uchar b, uchar a) : r_(r), g_(g), b_(b), a_(a)
    {
    }
    uchar * begin()
    {
      return &r_;
    }
    uchar * end()
    {
      return &a_ + 1;
    }
};

//  You will need to know how large a polygon is when you create it.
template<typename T = uint, uint S = 7>
class FixedArray
{
  public:
    FixedArray() : count_(0)
    {
    }
    FixedArray(uint cnt) : count_(cnt)
    {
      if (cnt > S)
      {
        ptr_ = new T[cnt];
      }
    }
    FixedArray(FixedArray const & other)
    {
      count_ = other.count_;
      if (count_ > S)
      {
        ptr_ = new T[count_];
      }
      std::copy(other.begin(), other.end(), (count_ > S) ? ptr_ : data_);
    }
    FixedArray & operator=(FixedArray const & other)
    {
      this->~FixedArray();
      new(this) FixedArray(other);
      return *this;
    }
    ~FixedArray()
    {
      if (count_ > S)
      {
        delete[] ptr_;
      }
    }
    T & operator[](size_t ix)
    {
      assert(ix < count_);
      return (count_ > S) ? ptr_[ix] : data_[ix];
    }
    T const & operator[](size_t ix) const
    {
      assert(ix < count_);
      return (count_ > S) ? ptr_[ix] : data_[ix];
    }
    T * begin()
    {
      return (count_ > S) ? ptr_ : data_;
    }
    T const * begin() const
    {
      return (count_ > S) ? ptr_ : data_;
    }
    T * end()
    {
      return (count_ > S) ? ptr_+count_ : &data_[count_];
    }
    T const * end() const
    {
      return (count_ > S) ? ptr_+count_ : &data_[count_];
    }
    size_t size() const
    {
      return count_;
    }
    T * rewrite(size_t cnt)
    {
      this->~FixedArray();
      new(this) FixedArray((uint)cnt);
      assert(count_ == cnt);
      return begin();
    }
    uint count_;
    T * ptr_;
    T data_[S];
};

class Poly
{
  public:
    Poly()
      : material_(0)
    {
    }
    Poly(uint lCount, uint vCount)
      : material_(0)
      , indices_(vCount)
      , coords_(lCount)
    {
    }
    void rewrite(uint lCount, uint vCount)
    {
      indices_.rewrite(vCount);
      coords_.rewrite(lCount);
      for (uint i = 0; i < lCount; ++i) {
        coords_[i].rewrite(vCount);
      }
    }
    uint faceSize() const
    {
      return (uint)indices_.size();
    }
    uint numCoords() const
    {
      return (uint)coords_.size();
    }
    FixedArray<uint> & indices()
    {
      return indices_;
    }
    FixedArray<uint> const & indices() const
    {
      return indices_;
    }
    FixedArray<Float2> & coords(uint i)
    {
      assert(i < coords_.size());
      return coords_[i];
    }
    FixedArray<Float2> const & coords(uint i) const
    {
      assert(i < coords_.size());
      return coords_[i];
    }
    uint material_;
    FixedArray<uint> indices_;
    FixedArray<FixedArray<Float2> > coords_;
};


template<typename T>
class IInterface
{
  public:
    //  interface for iterating items
    virtual size_t count() = 0;
    virtual T const * begin() = 0;
    virtual T const * end() = 0;
    virtual T const & item(size_t offset) = 0;
    //  for now, no interface for adding/updating items
};

class IEntity
{
  public:
    virtual char const * name() = 0;
};

class ILayer
{
  public:
    virtual int type() = 0;
    virtual char const * texture() = 0;
    virtual int blend() = 0;
};

class IMaterial : public IEntity
{
  public:
    virtual IInterface<ILayer *> * layers() = 0;
};

enum LightType {
  LT_POINTLIGHT = 1,
  LT_DIRECTIONALLIGHT = 2,
  LT_SPOTLIGHT = 3
};

struct PointLight {
  float constant;
  float linear;
  float quadratic;
  int range;
  Float3 center;
  int flash;
  int active;
  Color diffuse;
  Color specular;
  int intensity;
  int id;
  Float3 _reserved1;
  float _reserved2;
  float _reserved3;
  float _reserved4;
};
struct SpotLight {
  float constant;
  float linear;
  float quadratic;
  int range;
  Float3 center;
  int flash;
  int active;
  Color diffuse;
  Color specular;
  int intensity;
  int id;
  Float3 direction;
  float innerangle;
  float outerangle;
  float falloff;
};
struct DirectionalLight {
  Float3 _reserved1;
  int _reserved;
  Float3 center;
  int flash;
  int active;
  Color diffuse;
  Color specular;
  int intensity;
  int id;
  Float3 direction;
  float _reserved2;
  float _reserved3;
  float _reserved4;
};

class ILight : public IEntity
{
  public:
    virtual LightType type() = 0;
    virtual PointLight const & point() = 0;
    virtual DirectionalLight const & directional() = 0;
    virtual SpotLight const & spot() = 0;
};

class IObject : public IEntity
{
  public:
    virtual char const * name() = 0;
    virtual char const * tag() = 0;
    virtual IInterface<Float3> * vertices() = 0;
    virtual IInterface<Poly> * polygons() = 0;
};

class IScene
{
  public:
    //  The scene owns all the returned interfaces.
    virtual void dispose() = 0;
    virtual char const * name() = 0;
    virtual IInterface<IObject *> * objects() = 0;
    virtual IInterface<IMaterial *> * materials() = 0;
    virtual IInterface<ILight *> * lights() = 0;
};

//  The binary linkage to DeleD goes through this callback.
struct TCbData {    // callback data passed back to application (DeleD)
  TCbData(uint r)
  {
    reason_ = r;
    data_ = 0;
    size_ = 0;
  }
 	uint reason_;      // reason for callback
  char * data_;      // data to get or set
  unsigned size_;    // size of Data
};




template<typename T>
class AutoDispose
{
  public:
    explicit AutoDispose(T * t) throw() : t_(t)
    {
    }
    AutoDispose() throw() : t_(0)
    {
    }
    AutoDispose(AutoDispose<T> & o) throw() : t_(o.t_)
    {
      o.t_ = 0;
    }
    ~AutoDispose()
    {
      if (t_ != 0)
      {
        t_->dispose();
      }
    }
    T ** operator&()
    {
      assert(t_ == 0);
      return &t_;
    }
    AutoDispose & operator=(T * t)
    {
      if (t_ != 0)
      {
        t_->dispose();
      }
      t_ = t;
      return *this;
    }
    T * get() const throw()
    {
      return t_;
    }
    T * operator->() const throw()
    {
      return t_;
    }
    bool operator!() const
    {
      return t_ == 0;
    }
    
    T * t_;
};

#endif  //  DeleDAPI_h
